/*
	This file is part of AVRCppLib.

    AVRCppLib is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    AVRCppLib is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with AVRCppLib.  If not, see <http://www.gnu.org/licenses/>.

    Copyright (C) Maxime Lemonnier 2007-2011
 */
#include "../Utils/Logging.h"


#include <stdlib.h>
#include <alloca.h>

inline void* stackNewOp(size_t size)  __attribute__((always_inline));

void* stackNewOp(size_t size)
{
	LOCK_INTERRUPTS;

	if(distanceHeapFromStack() < size)
	{
		OUT_OF_STACK_MEMORY(size);
		return NULL;
	}

	void * memory = alloca(size);

	UNLOCK_INTERRUPTS;

	return memory;
}

inline void * newOp(size_t size)
{

	LOCK_INTERRUPTS;

	void* memory = malloc(size);

	updateHeapTop(size, memory);

#ifdef MEMORY_LOGGING
	mallocLog(size, memory);
#endif

	UNLOCK_INTERRUPTS;

	return memory;
}

inline void * callocOp(size_t size)
{

	LOCK_INTERRUPTS;

	void* memory = calloc(1, size);

	updateHeapTop(size, memory);

#ifdef MEMORY_LOGGING
	mallocLog(size, memory);
#endif

	UNLOCK_INTERRUPTS;

	return memory;
}

inline void* reallocOp(size_t size, void* memory)
{


	LOCK_INTERRUPTS;

	void* reallocatedMemory;

	if( __malloc_heap_start < (char*)memory && (char*)memory < getHeapTop())
		reallocatedMemory = realloc(memory, size);
	else
		reallocatedMemory = malloc(size);

	updateHeapTop(size, memory);

#ifdef MEMORY_LOGGING
	reallocLog(size, memory, reallocatedMemory);
#endif

	UNLOCK_INTERRUPTS;

	return reallocatedMemory;
}

inline void delOp(void* memory)
{

	LOCK_INTERRUPTS;

#ifdef MEMORY_LOGGING
	freeLog(memory);
#endif

	free(memory);

	UNLOCK_INTERRUPTS;
}

inline void * allocateOp(size_t size, Allocator allocator) __attribute__((always_inline));

void * allocateOp(size_t size, Allocator allocator)
{
	using namespace Allocators;

	switch(allocator)
	{
		case MALLOC:	return newOp(size);

		case CALLOC:	return callocOp(size);

		case STACK:		return stackNewOp(size);

		default :		return NULL;
	}
}

void* operator new(size_t size, Allocator allocator)
{
	return allocateOp(size, allocator);
}

void* operator new[](size_t size, Allocator allocator)
{
	return allocateOp(size, allocator);
}
